home *** CD-ROM | disk | FTP | other *** search
/ Night Owl 9 / Night Owl CD-ROM (NOPV9) (Night Owl Publisher) (1993).ISO / 020a / pc_set.zip / PC-SETUP.BAS next >
BASIC Source File  |  1993-01-29  |  40KB  |  1,071 lines

  1. '********** PC-SETUP.BAS - PC Magazine Install Utility
  2.  
  3. 'Copyright (c) 1992 Ethan Winer
  4.  
  5. 'Note: If you run this program in the QB editor the ExeName function will
  6. 'return the directory that QB.EXE was run from, which is not necessarily the
  7. 'current directory.  In that case you may have to enter the directory name
  8. 'where your various .ZIP files reside for PC-SETUP to find them.
  9. '
  10. 'If you are using Crescent's P.D.Q. you must search for all each call to the
  11. 'Interrupt routine, and change it as shown in the accompanying comments.  You
  12. 'can then compile and link this program for the smallest size possible as
  13. 'follows:
  14. '
  15. '   bc pc-setup /o/s;
  16. '   link /nod/noe/packc/far/ex _
  17. '     pc-setup _noread _noval _noerror _cprint , , nul , [basic7] pdq;
  18. '   exe2com pc-setup               (optional)
  19. '   del pc-setup.exe               (optional)
  20. '   ren pc-setup.com pc-setup.exe  (optional)
  21. '
  22. 'The PC-SETUP.EXE program supplied via PC MagNet was created with QuickBASIC
  23. '4.5 using the five steps shown above.
  24.  
  25.  
  26. DEFINT A-Z
  27.  
  28. '---- BASIC SUB and FUNCTION procedures in this program file
  29. '
  30. DECLARE SUB CopyFile (Source$)
  31. DECLARE SUB DrawBox (ULRow, ULCol, LRRow, LRCol, Style)
  32. DECLARE SUB DrawScreen ()
  33. DECLARE SUB Editor (Text$, Row, LeftCol, Length, KeyCode)
  34. DECLARE SUB EarlyEnd ()
  35. DECLARE SUB ErrorEnd (Message$)
  36. DECLARE SUB MidCharS (Work$, Position, NewChar)
  37. DECLARE SUB ReadNames (Spec$, Array$())
  38. DECLARE SUB SelectFiles (FileNames$(), Choice, ExitCode)
  39. DECLARE SUB SetDrive (Drive$)
  40. DECLARE SUB StuffBuf (Work$)
  41.  
  42. DECLARE FUNCTION ChangeDir% (DirName$)
  43. DECLARE FUNCTION DOSVersion% ()
  44. DECLARE FUNCTION Execute% (FileName$, Parameter$)
  45. DECLARE FUNCTION ExeName$ ()
  46. DECLARE FUNCTION FileCount% (FileSpec$, DirFlag)
  47. DECLARE FUNCTION GetComment$ (FileName$)
  48. DECLARE FUNCTION GetDir$ ()
  49. DECLARE FUNCTION GetDrive% ()
  50. DECLARE FUNCTION IntVal% (Work$)
  51. DECLARE FUNCTION MakeDir% (DirName$)
  52. DECLARE FUNCTION MidChar% (Work$, Position)
  53. DECLARE FUNCTION Prompt% (Which)
  54. DECLARE FUNCTION SourceDir$ ()
  55.  
  56.  
  57. 'Define the TYPE and other shared variables needed for using CALL InterruptX.
  58. '
  59. TYPE RegType
  60.   AX    AS INTEGER
  61.   BX    AS INTEGER
  62.   CX    AS INTEGER
  63.   DX    AS INTEGER
  64.   BP    AS INTEGER
  65.   SI    AS INTEGER
  66.   DI    AS INTEGER
  67.   Flags AS INTEGER
  68.   DS    AS INTEGER
  69.   ES    AS INTEGER
  70. END TYPE
  71. DIM SHARED Regs AS RegType
  72.  
  73. TYPE DTAType                            'used by find first/next service
  74.   Reserved  AS STRING * 21              'reserved for use by DOS
  75.   Attribute AS STRING * 1               'the file's attribute
  76.   FileTime  AS STRING * 2               'the file's time
  77.   FileDate  AS STRING * 2               'the file's date
  78.   FileSize  AS LONG                     'the file's size
  79.   FileName  AS STRING * 13              'the file's name
  80. END TYPE
  81. DIM SHARED DTA AS DTAType
  82.  
  83. DIM SHARED DOS                          'so the DOS procedures can get at it
  84. DIM SHARED ZBuffer AS STRING * 80       'holds ASCIIZ copies of DOS strings
  85. DIM SHARED One, Zero, Zero$             'these save code when used in CALLs
  86. DIM SHARED Temp, Temp$                  'these are reusable scratch variables
  87.  
  88. '---- Define some constants and variables, and colors based on display type.
  89. '
  90. CONST MaxFiles% = 19                    'max. number of .ZIP files per disk
  91. CONST DirLength% = 25                   'length of dest. directory display
  92.  
  93. One = 1                                 'saves four bytes per use in a CALL
  94. DOS = &H21                              'also saves four bytes per use
  95. Zero$ = CHR$(0)                         'call CHR$() just once here
  96. PadComment$ = SPACE$(36)                'holds each comment when printing
  97. REDIM DirsMade$(1 TO 100)               'remembers directories we created
  98.  
  99. Bar$ = "╠" + STRING$(78, 205) + "╣"     'for the main screen separating bars
  100. Msg$ = SPACE$(79)                       'for messages on the bottom line
  101. IF INSTR(UCASE$(COMMAND$), "/B") THEN MonoFlag = -1   '/b forces mono colors
  102.  
  103. NormFG = 11: NormBG = 7                 'assume colors for a color display
  104. HiFG = 11: HiBG = 4                     'menu and default directory colors
  105. MainFG = 10: MainBG = 1                 'main screen and box FG and BG colors
  106. CsrSize = 7                             'color displays use 8 scan lines
  107.  
  108. DEF SEG = 0                             'see if it's really a color display
  109. MonoMon = (PEEK(&H463) = &HB4)          'if not, MonoMon now equals -1
  110. IF MonoMon OR MonoFlag THEN             'it's monochrome or /b was used
  111.   NormFG = 7: NormBG = 0
  112.   HiFG = 15: HiBG = 0
  113.   MainFG = 0: MainBG = 7
  114.   IF MonoMon THEN CsrSize = 12          'mono displays use 13 scan lines
  115. END IF
  116.  
  117. IF DOSVersion% < 300 THEN               'PC-SETUP requires DOS 3.0 or later
  118.   PRINT "DOS 3.0 or later required."
  119.   END
  120. END IF
  121.  
  122. '---- Get the directory PC-SETUP was run from or prompt for it if needed, to
  123. '     ensure that there's at least one .ZIP file present to install.
  124. '
  125. InstPath$ = SourceDir$
  126. DO
  127.   IF RIGHT$(InstPath$, 1) <> "\" THEN InstPath$ = InstPath$ + "\"
  128.   InstSpec$ = InstPath$ + "*.ZIP"
  129.   NumFiles = FileCount%(InstSpec$, Zero)
  130.   IF NumFiles THEN EXIT DO
  131.   PRINT "No .ZIP files were found."
  132.   INPUT "Enter the source directory or press Enter to end: ", InstPath$
  133.   IF LEN(InstPath$) = 0 THEN END
  134. LOOP
  135.  
  136. IF FileCount%(InstPath$ + "PKUNZIP.EXE", Zero) = 0 THEN 'confirm PKUNZIP
  137.   PRINT "Can't find PKUNZIP."                           '  is available
  138.   END
  139. END IF
  140.      
  141. IF MidChar%(InstPath$, 2) <> 58 THEN    'if there's no drive letter (:)
  142.   InstPath$ = CHR$(GetDrive%) + ":" + InstPath$ 'append the current drive
  143. END IF
  144.  
  145.  
  146. '---- See if they're installing more than one disk, and if so how many.
  147. '
  148. NumDisks = 1                                'assume only one disk for now
  149. NumDisksFile$ = InstPath$ + "NUMDISKS.*"    'concatenate these just once
  150.  
  151. IF FileCount%(NumDisksFile$, Zero) THEN
  152.   DIM NumDisks$(1 TO 1)
  153.   CALL ReadNames(NumDisksFile$, NumDisks$())
  154.   Temp = INSTR(NumDisks$(1), ".")
  155.   NumDisks = IntVal%(MID$(NumDisks$(1), Temp + 1))
  156. END IF
  157.  
  158.  
  159. '---- See if there's a DEFAULT.DIR file in the root directory of the first
  160. '     distribution disk, and if so read its contents.  Here we're using
  161. '     FileCount to merely see if the file exists.  If there's no DEFAULT.DIR
  162. '     file, default to current drive and directory.  And if the current drive
  163. '     is A or B replace that with C.
  164. '
  165. SaveDir$ = CHR$(GetDrive%) + ":" + GetDir$      'save this while we have it
  166. DefaultDir$ = SaveDir$                          'now assign it as the default
  167.  
  168. Temp = ASC(DefaultDir$)                         'avoid using ASC() twice
  169. IF Temp = 65 OR Temp = 66 THEN                  'don't default to A: or B:
  170.   CALL MidCharS(DefaultDir$, 1, 67)             'if A: or B:, substitute C:
  171. END IF
  172.  
  173. DefaultDirFile$ = InstPath$ + "DEFAULT.DIR"     'concatenate these just once
  174.  
  175. IF FileCount%(DefaultDirFile$, Zero) THEN       'open the file if it exists
  176.   OPEN DefaultDirFile$ FOR INPUT AS #1
  177.   INPUT #1, DefaultDir$                         'read the default directory,
  178.   CLOSE                                         ' trim and capitalize (UCASE$
  179.   DefaultDir$ = UCASE$(RTRIM$(LTRIM$(DefaultDir$))) ' is for cosmetics only)
  180. END IF
  181.  
  182.  
  183. '---- See if there's a PROGRAM.RUN file in the root directory of the first
  184. '     distribution disk, and if so read its contents.
  185. '
  186. RunFileName$ = InstPath$ + "PROGRAM.RUN"        'concatenate these just once
  187. IF FileCount%(RunFileName$, Zero) THEN          'open the file if it exists
  188.   OPEN RunFileName$ FOR INPUT AS #1
  189.   INPUT #1, RunName$
  190.   CLOSE
  191.   RunName$ = RunName$ + CHR$(13)                'simulate pressing Enter
  192.   IF LEN(RunName$) > 15 THEN RunName$ = ""      'don't use name if too long
  193. END IF
  194.  
  195.  
  196. '---- This is the main installation loop that cycles through each diskette.
  197. '
  198. FOR Disk = 1 TO NumDisks
  199.  
  200.   '---- See how many .ZIP files there are on the current disk, and limit the
  201.   '     number we'll handle to MaxFiles% if there are more than that.  Then
  202.   '     draw/redraw the main screen.
  203.   '
  204.   NumFiles = FileCount%(InstSpec$, Zero)
  205.   IF NumFiles > MaxFiles% THEN NumFiles = MaxFiles%
  206.  
  207.   CALL DrawScreen
  208.  
  209.   '---- Read the .ZIP file names and display them in a vertical menu.  Then
  210.   '     read any default directories (if present) within each .ZIP file's
  211.   '     comment, and display them in the destination directory fields.  For
  212.   '     each .ZIP file that doesn't have a corresponding default directory
  213.   '     in the comment, use the contents of the main DEFAULT.DIR file found
  214.   '     in the root directory of the disk.
  215.   '
  216.   REDIM ZIPName$(1 TO NumFiles)
  217.   REDIM DestDir$(1 TO NumFiles)
  218.   REDIM Comment$(1 TO NumFiles)
  219.  
  220.   CALL ReadNames(InstSpec$, ZIPName$())
  221.  
  222.   FOR X = 1 TO NumFiles                 'look at each .ZIP file comment
  223.     DestDir$(X) = SPACE$(DirLength%)    'create a string to hold the dest dir
  224.     LSET DestDir$(X) = DefaultDir$      'assume none, use the global default
  225.     Comment$(X) = GetComment$(InstPath$ + ZIPName$(X))
  226.     Temp = INSTR(Comment$(X), "■")      'see if a directory was given
  227.     IF Temp THEN                        'there is a directory for this file
  228.       LSET DestDir$(X) = UCASE$(MID$(Comment$(X), Temp + 1)) 'dir is on right
  229.       Comment$(X) = LEFT$(Comment$(X), Temp - 1)         'and comment on left
  230.     END IF
  231.   NEXT
  232.  
  233.   FOR X = 1 TO NumFiles                 'add leading blanks to make room
  234.     ZIPName$(X) = "   " + ZIPName$(X)   '  for the CHR$(251) check marks
  235.     IF RIGHT$(Comment$(X), 1) = "√" THEN    'they want this file checked
  236.       CALL MidCharS(ZIPName$(X), 2, 251)    'so check it in the file list box
  237.       Comment$(X) = LEFT$(Comment$(X), LEN(Comment$(X)) - 1)
  238.     END IF
  239.     LSET PadComment$ = Comment$(X)      'display the directories and comments
  240.     COLOR NormFG, NormBG                'while we're here
  241.     LOCATE X + 4, 2: PRINT DestDir$(X);
  242.     LOCATE X + 4, 44: PRINT PadComment$;
  243.   NEXT
  244.  
  245.   DO                                    'let the user select the files
  246.     CALL SelectFiles(ZIPName$(), Choice, ExitCode)
  247.     IF ExitCode = 9 THEN                'they pressed Tab
  248.       COLOR MainFG, MainBG
  249.       LOCATE 25, 2
  250.       PRINT SPC(30); "Tab: Select .ZIP files    F2: Begin    Esc: Quit";
  251.  
  252.       DO
  253.         CALL Editor(DestDir$(Choice), Choice + 4, 2, 25, ExitCode)
  254.         SELECT CASE ExitCode            'how did the terminate editing?
  255.           CASE -80                      'Down Arrow
  256.             Choice = Choice + 1         'wrap around if they go past the end
  257.             IF Choice > NumFiles THEN Choice = 1
  258.           CASE -72                      'Up Arrow
  259.             Choice = Choice - 1         'wrap to the end if they go before 1
  260.             IF Choice < 1 THEN Choice = NumFiles
  261.           CASE 27                       'Escape
  262.             CALL EarlyEnd
  263.           CASE -60                      'F2
  264.             ExitCode = -60              'tell SelectFiles to come right back
  265.             EXIT DO                     '  so we can exit both levels of DO
  266.           CASE ELSE
  267.             EXIT DO                     'anything else returns to SelectFiles
  268.         END SELECT
  269.       LOOP
  270.     ELSEIF ExitCode = -60 THEN          'F2
  271.       ExitCode = 0                      'prevent unwanted recursion across
  272.       EXIT DO                           '  multiple disks
  273.     ELSEIF ExitCode = 27 THEN           'Escape
  274.       CALL EarlyEnd
  275.     END IF
  276.   LOOP
  277.  
  278.  
  279.   '---- Install the selected files to the specified destination directories.
  280.   '     For each file that is tagged, either change to the appropriate drive
  281.   '     and directory, or ensure that we're back to the original path.
  282.   '
  283.   FOR X = 1 TO NumFiles                       'for each .ZIP file present
  284.  
  285.     IF MidChar%(ZIPName$(X), 2) = 251 THEN    'if it's tagged to install
  286.  
  287.       IF X = 1 THEN RunProg = -1              'use PROGRAM.RUN only if first
  288.                                               '  file is being installed
  289.       COLOR MainFG, MainBG                    'for the status message below
  290.       CLS
  291.       LSET Msg$ = "Installing" + RTRIM$(MID$(ZIPName$(X), 3)) + "..."
  292.       COLOR HiFG
  293.       PRINT Msg$                              'advise the user as to progress
  294.       COLOR MainFG
  295.      
  296.       DestPath$ = RTRIM$(DestDir$(X))         'work with a copy of the path
  297.  
  298.       IF MidChar%(DestPath$, 2) = 58 THEN     'if a drive was used (58 = ":")
  299.         CALL SetDrive(DestPath$)              'change to that drive
  300.         IF GetDrive% <> ASC(DestPath$) THEN   'no such drive
  301.           CALL ErrorEnd("Drive " + LEFT$(DestPath$, 2) + " invalid")
  302.         END IF
  303.       ELSE
  304.         CALL SetDrive(DefaultDir$)            'else switch to default drive
  305.       END IF
  306.  
  307.       'strip off possible trailing "\" unless it refers to the root directory
  308.       IF RIGHT$(DestPath$, 1) = "\" THEN
  309.         Temp = LEN(DestPath$)
  310.         IF Temp > 1 AND RIGHT$(DestPath$, 2) <> ":\" THEN
  311.           DestPath$ = LEFT$(DestPath$, Temp - 1)
  312.         END IF
  313.       END IF
  314.  
  315.       Temp = -1                               'assume directory now exists
  316.       IF LEN(DestPath$) THEN                  'if a directory name was given
  317.  
  318.         IF RIGHT$(DestPath$, 1) <> "\" THEN   'and it's not a root directory
  319.  
  320.           IF FileCount%(DestPath$, -1) = 0 THEN   'does the directory exist?
  321.             IF MakeDir%(DestPath$) THEN       'no, so first try to create it
  322.               CALL ErrorEnd("Cannot create " + DestPath$)
  323.             END IF
  324.             Temp = 0                          'it can't possibly have files
  325.             DirsWeMade = DirsWeMade + 1       'show we created another one
  326.             DirsMade$(DirsWeMade) = DestPath$ 'and remember its name
  327.           END IF
  328.  
  329.           FOR Y = 1 TO DirsWeMade             'see if we made this directory
  330.             IF DestPath$ = DirsMade$(Y) THEN  'yes, so there's no need to
  331.               Temp = 0                        ' warn about overwriting files
  332.               EXIT FOR
  333.             END IF
  334.           NEXT
  335.  
  336.         END IF
  337.  
  338.         IF ChangeDir%(DestPath$) THEN         'then try to change to it
  339.           CALL ErrorEnd("Unable to access " + DestPath$)
  340.         END IF
  341.  
  342.       END IF
  343.  
  344.       PKCmd$ = "-o "                          'assume they want to be warned
  345.       IF Temp THEN                            'this directory existed
  346.         IF Prompt%(Zero) THEN                 'ask if they want to be warned
  347.           PKCmd$ = ""                         ' to overwrite existing files
  348.         END IF                                ' without further prompting and
  349.       END IF                                  ' use appropriate command if so
  350.  
  351.       IF NOT PKCopied THEN                    'copy PKUNZIP first time only
  352.         PKCopied = -1                         'flag that we did it already
  353.         CALL CopyFile(InstPath$)              'show where PKUNZIP.EXE is
  354.         PKDir$ = RTRIM$(DestDir$(X))          'remember where we put it!
  355.         IF LEN(PKDir$) = 0 THEN PKDir$ = DefaultDir$ 'use default dir if none
  356.        
  357.         Temp = ASC(RIGHT$(PKDir$, 1))         'check the right-most character
  358.         IF Temp <> 58 AND Temp <> 92 THEN     'if not a colon or backslash
  359.           PKDir2$ = "\"                       ' create a trailing "\"
  360.         END IF                                ' which is appended below
  361.       END IF
  362.  
  363.       '---- Install all of the files contained in this .ZIP file and check
  364.       '     for an errors returned by either DOS or PKUNZIP.  Execute returns
  365.       '     positive error values if PKUNZIP was run okay but it returned an
  366.       '     error via the DOS Errorlevel.  If DOS itself reports an error
  367.       '     (perhaps there wasn't enough memory to run the program) Execute
  368.       '     returns the DOS error value as a negative number.  Error 8 is the
  369.       '     DOS "Out of memory" error.
  370.       '
  371.       Temp = Execute%(PKDir$ + PKDir2$ + "PKUNZIP.EXE", PKCmd$ + InstPath$ + RTRIM$(MID$(ZIPName$(X), 4)))
  372.       IF Temp THEN
  373.         Temp$ = "PKUNZIP reports Error" + STR$(Temp)
  374.         IF Temp < 0 THEN Temp$ = "Out of memory"
  375.         CALL ErrorEnd(Temp$)
  376.       END IF
  377.  
  378.       IF INKEY$ = CHR$(27) THEN         'allow aborting by pressing Escape
  379.         CALL EarlyEnd
  380.       END IF
  381.  
  382.     END IF
  383.  
  384.   NEXT
  385.  
  386.   IF Disk < NumDisks THEN               'if there are more disks to install
  387.     LOCATE 25, 2                        'prompt to insert the next disk
  388.     LSET Msg$ = "Insert the next disk and press any key when ready"
  389.     COLOR MainFG, MainBG
  390.     PRINT Msg$;
  391.     LOCATE , 52
  392.     DO: LOOP WHILE LEN(INKEY$)          'first clear any pending keys
  393.     DO: LOOP UNTIL LEN(INKEY$)          'then wait for a keypress
  394.     CALL SetDrive(SaveDir$)             'return to the current drive and its
  395.     Temp = ChangeDir%(SaveDir$)         '  current directory before going on
  396.   END IF
  397.  
  398. NEXT Disk
  399.  
  400.  
  401. '---- Report success and run the specified program (StuffBuf ignores a null
  402. '     string argument.
  403. '
  404. COLOR 7, 0: CLS
  405. LOCATE 13, 30: PRINT "Installation complete!"
  406. LOCATE 22, 1, 1
  407. CALL SetDrive(PKDir$)                   'change to the first drive and
  408. Temp = ChangeDir%(PKDir$)               ' directory we installed to
  409. KILL "PKUNZIP.EXE"                      'delete the copy of PKUNZIP.EXE there
  410. IF RunProg THEN CALL StuffBuf(RunName$) 'stuff the buffer if appropriate
  411. END                                     'and end
  412.  
  413. FUNCTION ChangeDir% (DirName$)          'returns 0 if Okay, -1 if an error
  414.  
  415.   ZBuffer$ = DirName$ + Zero$           'make an ASCIIZ string
  416.   Regs.AX = &H3B00                      'DOS change directory service
  417.   Regs.DX = VARPTR(ZBuffer$)            'show DOS where ZBuffer$ is
  418.   CALL Interrupt(DOS, Regs, Regs)       'call DOS
  419.  'CALL Interrupt(DOS, Regs)             'use this with P.D.Q.
  420.  
  421.   IF Regs.Flags AND 1 THEN              'must be an invalid path
  422.     ChangeDir% = -1                     'return -1 as an error
  423.   END IF
  424.  
  425. END FUNCTION
  426.  
  427. SUB CopyFile (Source$) STATIC           'copies PKUNZIP.EXE
  428.  
  429.   Temp$ = Source$ + "PKUNZIP.EXE"
  430.  
  431.   IF FileCount%(Temp$, Zero) THEN
  432.     OPEN Temp$ FOR BINARY AS #1         'open the input file if it exists
  433.   ELSE                                  'if we can't find it, bag out with
  434.     CALL ErrorEnd("Can't find PKUNZIP.EXE")     ' an error message
  435.   END IF
  436.  
  437.   OPEN "PKUNZIP.EXE" FOR BINARY AS #2   'now open the target file
  438.  
  439.   Temp$ = SPACE$(LOF(1))                'make a buffer to hold PKUNZIP.EXE
  440.   GET #1, , Temp$                       'read the source file
  441.   PUT #2, , Temp$                       'write it to the destination
  442.  
  443.   CLOSE                                 'all done here
  444.    
  445. END SUB
  446.  
  447. FUNCTION DOSVersion% STATIC         'returns DOS version * 100 (3.30 = 330)
  448.  
  449.   Regs.AX = &H3000                  'DOS get DOS version service
  450.   CALL Interrupt(DOS, Regs, Regs)
  451.  'CALL Interrupt(DOS, Regs)         'use this with P.D.Q.
  452.  
  453.   'combine the major version in AL and the minor in AH
  454.   DOSVersion% = (Regs.AX AND 255) * 100 + (Regs.AX \ 256)
  455.  
  456. END FUNCTION
  457.  
  458. SUB DrawBox (ULRow, ULCol, LRRow, LRCol, Style) STATIC
  459.  
  460.   Length = LRCol - ULCol + 1            'calculate this just once
  461.  
  462.   IF Style = 1 THEN
  463.     LineType = 196
  464.     VertBar$ = "│"
  465.   ELSE
  466.     LineType = 205
  467.     VertBar$ = "║"
  468.   END IF
  469.  
  470.   FOR X = ULRow TO LRRow                'first draw the walls
  471.     LOCATE X, ULCol
  472.     Temp = 32
  473.     IF X = ULRow OR X = LRRow THEN Temp = LineType
  474.     PRINT VertBar$; STRING$(Length - 2, Temp); VertBar$;
  475.   NEXT
  476.  
  477.   IF Style = 1 THEN                     'then draw the corners
  478.     LOCATE ULRow, ULCol: PRINT "┌";
  479.     LOCATE ULRow, LRCol: PRINT "┐";
  480.     LOCATE LRRow, ULCol: PRINT "└";
  481.     LOCATE LRRow, LRCol: PRINT "┘";
  482.   ELSE
  483.     LOCATE ULRow, ULCol: PRINT "╔";
  484.     LOCATE ULRow, LRCol: PRINT "╗";
  485.     LOCATE LRRow, ULCol: PRINT "╚";
  486.     LOCATE LRRow, LRCol: PRINT "╝";
  487.   END IF
  488.  
  489. END SUB
  490.  
  491. SUB DrawScreen STATIC
  492.  
  493.   SHARED MainFG, MainBG, Bar$
  494.  
  495.   '---- Draw the title screen and surrounding boxes.
  496.   '
  497.   COLOR MainFG, MainBG: CLS : LOCATE , , 0
  498.   CALL DrawBox(One, One, 24, 80, 2)
  499.   LOCATE 2, 24: PRINT "PC Magazine's PC-SETUP Version 1.00"
  500.   LOCATE 3, 1: PRINT Bar$
  501.  
  502.   CALL DrawBox(3, 27, 24, 43, One)
  503.   LOCATE 3, 27:  PRINT "╤═══════════════╤";
  504.   LOCATE 24, 27: PRINT "╧═══════════════╧";
  505.  
  506.   LOCATE 4, 3: PRINT "Destination Directories";
  507.   LOCATE , 31: PRINT "ZIP Files";
  508.   LOCATE , 58: PRINT "Comments"
  509.  
  510.   LOCATE 25, 2
  511.   PRINT "Up/Down/Space: Select files    Tab: Edit destination    F2: Begin    Esc: Quit";
  512.  
  513. END SUB
  514.  
  515. SUB EarlyEnd STATIC
  516.  
  517.   IF Prompt%(One) THEN
  518.     COLOR 7, 0
  519.     CLS
  520.     LOCATE 24, , 1
  521.     END
  522.   END IF
  523.   LOCATE , , 0
  524.  
  525. END SUB
  526.  
  527. SUB Editor (Text$, Row, LeftCol, Length, KeyCode) STATIC
  528.  
  529.   SHARED HiFG, HiBG, NormFG, NormBG, MonoMon, CsrSize
  530.  
  531.   '----- Work with a temporary copy.
  532.   Edit$ = SPACE$(Length)
  533.   LSET Edit$ = Text$
  534.  
  535.   '----- See where to begin editing and print the string.
  536.   TxtPos = 1
  537.   LOCATE Row, LeftCol, 1, CsrSize - 1, CsrSize
  538.   COLOR HiFG, HiBG
  539.   PRINT Edit$;
  540.  
  541.   '----- This is the main loop for handling key presses.
  542.   DO
  543.      LOCATE , LeftCol + TxtPos - 1, 1
  544.  
  545.      DO
  546.        Ky$ = UCASE$(INKEY$)
  547.      LOOP UNTIL LEN(Ky$)        'wait for a keypress
  548.  
  549.      IF LEN(Ky$) = 1 THEN       'create a key code
  550.        KeyCode = ASC(Ky$)       'regular character key
  551.      ELSE                       'extended key
  552.        KeyCode = -ASC(RIGHT$(Ky$, 1))
  553.      END IF
  554.  
  555.      '----- Branch according to the key pressed.
  556.      SELECT CASE KeyCode
  557.  
  558.        '----- Backspace: decrement the pointer and the
  559.        '      cursor, and ignore if in the first column.
  560.        CASE 8
  561.          TxtPos = TxtPos - 1
  562.          IF TxtPos < 1 THEN TxtPos = 1
  563.          LOCATE , LeftCol + TxtPos - 1, 0
  564.          IF TxtPos > 0 THEN
  565.            IF InsStatus THEN
  566.              MID$(Edit$, TxtPos) = MID$(Edit$, TxtPos + 1) + " "
  567.            ELSE
  568.              MID$(Edit$, TxtPos) = " "
  569.            END IF
  570.            PRINT MID$(Edit$, TxtPos);
  571.          END IF
  572.  
  573.        '----- Enter or Escape: this block is optional in
  574.        '      case you want to handle these separately.
  575.        CASE 13, 27
  576.          EXIT DO                'exit the subprogram
  577.  
  578.        '----- Letter keys: turn off the cursor to hide
  579.        '      the printing, handle Insert mode as needed.
  580.        CASE 32 TO 254
  581.          LOCATE , , 0
  582.          IF InsStatus THEN      'expand the string
  583.            MID$(Edit$, TxtPos) = Ky$ + MID$(Edit$, TxtPos)
  584.            PRINT MID$(Edit$, TxtPos);
  585.          ELSE                   'else insert character
  586.            MID$(Edit$, TxtPos) = Ky$
  587.            PRINT Ky$;
  588.          END IF
  589.          TxtPos = TxtPos + 1    'update position counter
  590.          IF TxtPos > Length THEN TxtPos = Length
  591.  
  592.        '----- Left arrow: decrement the position counter.
  593.        CASE -75
  594.          TxtPos = TxtPos - 1
  595.          IF TxtPos < 1 THEN TxtPos = 1
  596.  
  597.        '----- Right arrow: increment position counter.
  598.        CASE -77
  599.          TxtPos = TxtPos + 1
  600.          IF TxtPos > Length THEN TxtPos = Length
  601.  
  602.        '----- Home: jump to the first character position.
  603.        CASE -71
  604.          TxtPos = 1
  605.  
  606.        '----- End: search for the last non-blank, and
  607.        '      make that the current editing position.
  608.        CASE -79
  609.          FOR N = Length TO 1 STEP -1
  610.            IF MID$(Edit$, N, 1) <> " " THEN EXIT FOR
  611.          NEXT
  612.          TxtPos = N + 1
  613.          IF TxtPos > Length THEN TxtPos = Length
  614.  
  615.        '----- Insert key: toggle the Insert state and
  616.        '      adjust the cursor size.
  617.        CASE -82
  618.          InsStatus = NOT InsStatus
  619.          IF InsStatus THEN
  620.            LOCATE , , , CsrSize \ 2, CsrSize
  621.          ELSE
  622.            LOCATE , , , CsrSize - 1, CsrSize
  623.          END IF
  624.  
  625.        '----- Delete: delete the current character and
  626.        '      reprint what remains in the string.
  627.        CASE -83
  628.          MID$(Edit$, TxtPos) = MID$(Edit$, TxtPos + 1) + " "
  629.          LOCATE , , 0
  630.          PRINT MID$(Edit$, TxtPos);
  631.  
  632.        '---- All other keys: exit the subprogram
  633.        CASE ELSE
  634.          EXIT DO
  635.      END SELECT
  636.  
  637.   '----- Loop until the cursor moves out of the field.
  638.   LOOP
  639.  
  640.   LSET Edit$ = LTRIM$(Edit$)    'trim and reprint the text in the normal
  641.   LOCATE , 2                    ' color before returning
  642.   COLOR NormFG, NormBG
  643.   PRINT Edit$
  644.  
  645.   Text$ = RTRIM$(Edit$)         'now trim what's on the right too
  646.  
  647. END SUB
  648.  
  649. SUB ErrorEnd (Message$) STATIC
  650.  
  651.   COLOR 7, 0
  652.   CLS
  653.   LOCATE 13, 34 - LEN(Message$) \ 2, 1
  654.   PRINT "Error: "; Message$; ", ending."
  655.   END
  656.  
  657. END SUB
  658.  
  659. FUNCTION Execute% (Program$, Parameter$) STATIC
  660.  
  661.   '---- Prepare the program name and parameter strings for processing.  DOS
  662.   '     requires that the parameter string hold the length of the parameter
  663.   '     text, followed by the parameter text, and then followed by a CHR$(13)
  664.   '     Enter byte.  The parameter block holds two CHR$(0) bytes followed by
  665.   '     the address and segment of the parameter string.
  666.   '
  667.   DIM Block AS STRING * 14         'this is the DOS parameter block
  668.   DIM Parm AS STRING * 50          'and this is the actual parameter text
  669.  
  670.   ZBuffer$ = Program$ + Zero$      'make an ASCIIZ string for DOS
  671.  
  672.   LSET Parm$ = CHR$(LEN(Parameter$)) + Parameter$ + CHR$(13)
  673.   LSET Block$ = Zero$ + Zero$ + MKI$(VARPTR(Parm$)) + MKI$(VARSEG(Parm$))
  674.  
  675.   Dummy& = SETMEM(-500000)         'free up memory for PKUNZIP to run
  676.  
  677.   Regs.AX = &H4B00                 'DOS load/execute function
  678.   Regs.DX = VARPTR(ZBuffer$)       'offset of program name into DX
  679.   Regs.ES = VARSEG(Block$)         'segment of parameter block into ES
  680.   Regs.BX = VARPTR(Block$)         'offset of parameter block into BX
  681.   Regs.DS = -1                     'set DS to BASIC's segment
  682.   CALL InterruptX(DOS, Regs, Regs) 'execute it as subordinate process
  683.  'CALL InterruptX(DOS, Regs)       'use this with P.D.Q.
  684.  
  685.   IF Regs.Flags AND 1 THEN         'DOS had an error trying to run PKUNZIP
  686.     Execute% = -Regs.AX            'set function value to exit code
  687.     EXIT FUNCTION
  688.   END IF
  689.  
  690.   Regs.AX = &H4D00                 'retrieve subordinate process code
  691.   CALL Interrupt(DOS, Regs, Regs)
  692.  'CALL Interrupt(DOS, Regs)        'use this with P.D.Q.
  693.   Execute% = Regs.AX               'set function value to exit code
  694.  
  695.   Dummy& = SETMEM(500000)          'reclaim the memory reliquished eariler
  696.  
  697. END FUNCTION
  698.  
  699. FUNCTION ExeName$ STATIC
  700.  
  701.   'Returns the name of the currently running program; requires DOS 3.0 +
  702.  
  703.   '---- DOS Interrupt &H21 service &H62 returns the PSP segment in BX
  704.   Regs.AX = &H6200
  705.   CALL Interrupt(DOS, Regs, Regs)
  706.  'CALL Interrupt(DOS, Regs)             'use this with P.D.Q.
  707.  
  708.   '---- The environment segment is at address &H2C/&H2D in PSP segment
  709.   DEF SEG = Regs.BX
  710.   DEF SEG = PEEK(&H2C) + PEEK(&H2D) * 256
  711.  
  712.   '---- Search the environment segment for two zero bytes in a row.  A count
  713.   '     word (which we skip over) follows that, and the program name follows
  714.   '     the count word.
  715.   Byte = 0
  716.   DO
  717.     IF PEEK(Byte) = 0 THEN              'this is zero
  718.       IF PEEK(Byte + 1) = 0 THEN        'this is too
  719.         Byte = Byte + 2                 'so skip both
  720.         EXIT DO                         'all done
  721.       END IF
  722.     END IF                              'else,
  723.     Byte = Byte + 1                     'keep looking
  724.   LOOP
  725.  
  726.   IF PEEK(Byte) = 1 THEN                'if this count byte = 1
  727.     Byte = Byte + 2                     'the name follows
  728.     DO WHILE PEEK(Byte)                 'up to another zero
  729.       Tmp$ = Tmp$ + CHR$(PEEK(Byte))    'this is a different Tmp$ on purpose
  730.       Byte = Byte + 1
  731.     LOOP
  732.     ExeName$ = Tmp$                     'assign the function output
  733.   END IF
  734.  
  735. END FUNCTION
  736.  
  737. FUNCTION FileCount% (FileSpec$, DirFlag)
  738.  
  739.   Regs.DX = VARPTR(DTA)                 'set new DTA address
  740.   Regs.AX = &H1A00                      'specify service 1Ah
  741.   CALL Interrupt(DOS, Regs, Regs)       'DOS set DTA service
  742.  'CALL Interrupt(DOS, Regs)             'use this with P.D.Q.
  743.  
  744.   Temp = 0                              'clear the counter
  745.   ZBuffer$ = FileSpec$ + Zero$          'make an ASCIIZ string
  746.  
  747.   Regs.DX = VARPTR(ZBuffer$)            'the file spec address
  748.   Regs.CX = 39                          'file attribute = all files
  749.   IF DirFlag THEN Regs.CX = 39 OR 16    'include directories too
  750.   Regs.AX = &H4E00                      'find first matching name service
  751.  
  752.   DO
  753.     CALL Interrupt(DOS, Regs, Regs)     'see if there's a match
  754.    'CALL Interrupt(DOS, Regs)           'use this with P.D.Q.
  755.     IF Regs.Flags AND 1 THEN EXIT DO    'no more files
  756.  
  757.     IF DirFlag THEN                     'do we want directories?
  758.       IF ASC(DTA.Attribute) AND 16 THEN 'yes, but is this a directory?
  759.         IF ASC(DTA.FileName) <> 46 THEN 'filter "." and ".." (46 = period)
  760.           Temp = Temp + 1               'we got another directory name
  761.         END IF
  762.       END IF
  763.     ELSE
  764.       Temp = Temp + 1                   'we got another file name
  765.     END IF
  766.      
  767.     Regs.AX = &H4F00                    'find next name service
  768.   LOOP
  769.  
  770.   FileCount% = Temp                     'assign the function output
  771.  
  772. END FUNCTION
  773.  
  774. FUNCTION GetComment$ (Zip$) STATIC      'read comment from file named in Zip$
  775.  
  776.   ZipID$ = "PK" + CHR$(5) + CHR$(6)     'this identifies a file as a ZIP file
  777.  
  778.   OPEN RTRIM$(Zip$) FOR BINARY AS #1    'open the .ZIP file
  779.   FileSize& = LOF(1)                    'get and save its length
  780.   BufferSize = 3072                     'the default header size
  781.   IF BufferSize > FileSize& THEN BufferSize = FileSize&
  782.   Temp$ = SPACE$(BufferSize)            'make buffer to receive ZIP header
  783.  
  784.   GET #1, FileSize& - BufferSize + 1, Temp$
  785.   CLOSE
  786.  
  787.   Temp = 0                              'find the last occurrence of PK ID
  788.   DO
  789.      HeaderOffset = Temp                'remember where this one is
  790.      Temp = INSTR(Temp + 1, Temp$, ZipID$)      'find the next one
  791.   LOOP WHILE Temp                               'until no more
  792.  
  793.   IF HeaderOffset THEN                  'if there's a comment, extract it
  794.     CommentLen = CVI(MID$(Temp$, HeaderOffset + 20, 2))
  795.     GetComment$ = MID$(Temp$, HeaderOffset + 22, CommentLen)
  796.   END IF
  797.  
  798.   Temp$ = ""                            'free up the memory
  799.  
  800. END FUNCTION
  801.  
  802. FUNCTION GetDir$ STATIC
  803.  
  804.   Regs.AX = &H4700                      'DOS get directory service
  805.   Regs.DX = 0                           'the drive goes in DL, 0 = default
  806.   Regs.SI = VARPTR(ZBuffer$)            'show DOS where ZBuffer$ is
  807.   CALL Interrupt(DOS, Regs, Regs)       'call DOS
  808.  'CALL Interrupt(DOS, Regs)             'use this with P.D.Q.
  809.  
  810.   IF Regs.Flags AND 1 THEN              'must be an invalid drive
  811.     GetDir$ = ""
  812.   ELSE
  813.     Temp = INSTR(ZBuffer$, Zero$)       'find the zero byte, and return only
  814.     GetDir$ = "\" + LEFT$(ZBuffer$, Temp - 1) ' what precedes it
  815.   END IF
  816.  
  817. END FUNCTION
  818.  
  819. FUNCTION GetDrive% STATIC
  820.  
  821.   Regs.AX = &H1900                      'DOS Get Current Drive service
  822.   CALL Interrupt(DOS, Regs, Regs)       'call DOS
  823.  'CALL Interrupt(DOS, Regs)             'use this with P.D.Q.
  824.   GetDrive% = (Regs.AX AND 255) + 65    'drive returned in AL as 0=A, 1=B...
  825.  
  826. END FUNCTION
  827.  
  828. FUNCTION IntVal% (Work$) STATIC
  829.  
  830.  'IntVal is an integer-only VAL substitute that reduces .EXE size up to 10K
  831.  
  832.   Length = LEN(RTRIM$(Work$))
  833.   Value = 0
  834.  
  835.   FOR X = Length TO 1 STEP -1
  836.     Temp = MidChar%(Work$, X)
  837.     IF Temp > 47 AND Temp < 58 THEN
  838.       IF X = Length THEN
  839.         Value = Temp - 48
  840.       ELSE
  841.         Value = Value + (Temp - 48) * 10
  842.       END IF
  843.     END IF
  844.   NEXT
  845.  
  846.   IntVal% = Value
  847.  
  848. END FUNCTION
  849.  
  850. FUNCTION MakeDir% (DirName$) STATIC
  851.  
  852.   ZBuffer$ = DirName$ + Zero$           'make an ASCIIZ string
  853.   Regs.AX = &H3900                      'DOS create directory service
  854.   Regs.DX = VARPTR(ZBuffer$)            'show DOS where ZBuffer$ is
  855.   CALL Interrupt(DOS, Regs, Regs)       'call DOS
  856.  'CALL Interrupt(DOS, Regs)             'use this with P.D.Q.
  857.  
  858.   IF Regs.Flags AND 1 THEN              'must be an invalid drive or bad name
  859.     MakeDir% = -1                       'return -1 as an error
  860.   END IF
  861.  
  862. END FUNCTION
  863.  
  864. FUNCTION MidChar% (Work$, Position)
  865.  
  866.   IF Position <= LEN(Work$) THEN
  867.     MidChar% = ASC(MID$(Work$, Position, 1))
  868.   ELSE
  869.     MidChar% = -1
  870.   END IF
  871.  
  872. END FUNCTION
  873.  
  874. SUB MidCharS (Work$, Position, NewChar) STATIC
  875.  
  876.   MID$(Work$, Position, 1) = CHR$(NewChar)
  877.  
  878. END SUB
  879.  
  880. FUNCTION Prompt% (Which) STATIC
  881.  
  882.   SHARED HiFG, HiBG, MonoMon, CsrSize, DestPath$
  883.  
  884.   DEF SEG = &HB800                      'assume a color display
  885.   IF MonoMon THEN DEF SEG = &HB000      'nope, use the mono video segment
  886.  
  887.   REDIM SaveScrn(10 TO 13, 14 TO 66)    'this saves the underlying screen
  888.   FOR Row = 10 TO 13                    'Here, Row and Col are zero-based
  889.     FOR Col = 14 TO 66
  890.       Temp = Row * 160 + Col * 2        'calculate the address just once
  891.       SaveScrn(Row, Col) = PEEK(Temp) + 256 * PEEK(Temp + 1)
  892.     NEXT
  893.   NEXT
  894.  
  895.   COLOR HiFG, HiBG
  896.   CALL DrawBox(11, 15, 14, 67, One)     'draw the surrounding box
  897.   IF Which THEN                         'we were called from EarlyEnd
  898.     LOCATE 12, 31                       'show this directory name
  899.     PRINT "Are you sure you want";      'print the prompt message
  900.     LOCATE 13, 29, 1, CsrSize - 1, CsrSize
  901.     PRINT "to quit installing? (Y/N) ";
  902.   ELSE                                  'prompt if okay to overwrite files
  903.     LOCATE 12, 17                       'show this directory name
  904.     PRINT "Installing to "; DestPath$   'print the prompt message
  905.     LOCATE 13, 17, 1, CsrSize - 1, CsrSize
  906.     PRINT "Prompt before overwriting existing files? (Y/N) ";
  907.   END IF
  908.   DO                                    'wait for Yes or No (only)
  909.     Temp$ = UCASE$(INKEY$)
  910.   LOOP UNTIL INSTR(" YN", Temp$) > 1
  911.   Prompt% = 0                           'assume the answer is No
  912.   IF Temp$ = "Y" THEN Prompt% = -1      'they answered Yes
  913.  
  914.   FOR Row = 10 TO 13                    'now restore the screen
  915.     FOR Col = 14 TO 66                  'as above
  916.       Temp = Row * 160 + Col * 2
  917.       POKE Temp, SaveScrn(Row, Col) AND 255
  918.       POKE Temp + 1, SaveScrn(Row, Col) \ 256
  919.     NEXT
  920.   NEXT
  921.  
  922.   ERASE SaveScrn
  923.   LOCATE 2                              'put cursor at the top of the screen
  924.  
  925. END FUNCTION
  926.  
  927. SUB ReadNames (Spec$, Array$()) STATIC  'reads file names into an array
  928.  
  929.   ZBuffer$ = Spec$ + Zero$              'make an ASCIIZ string of the spec
  930.   CurFile = 0                           'zero out the file counter
  931.  
  932.   Regs.DX = VARPTR(ZBuffer$)            'the file spec address
  933.   Regs.CX = 39                          'file attribute = all files
  934.   Regs.AX = &H4E00                      'find first matching name service
  935.  
  936.   DO
  937.     CALL Interrupt(DOS, Regs, Regs)     'see if there's a match
  938.    'CALL Interrupt(DOS, Regs)           'use this with P.D.Q.
  939.     IF Regs.Flags AND 1 THEN EXIT DO    'no more files
  940.  
  941.     CurFile = CurFile + 1               'we found another file name
  942.     Array$(CurFile) = SPACE$(12)        'create the string to hold it
  943.     Temp$ = DTA.FileName                'assign the name
  944.     Temp = INSTR(Temp$, Zero$)          'find the terminating zero byte
  945.     LSET Array$(CurFile) = LEFT$(Temp$, Temp - 1) 'keep only what precedes it
  946.  
  947.     Regs.AX = &H4F00                    'find the next name
  948.   LOOP
  949.  
  950. END SUB
  951.  
  952. SUB SelectFiles (FileNames$(), Choice, ExitCode) STATIC
  953.  
  954.   SHARED NumFiles, NormFG, NormBG, HiFG, HiBG, MainFG, MainBG
  955.  
  956.   IF ExitCode = -60 THEN EXIT SUB       'we got here via F2 pressed in Editor
  957.  
  958.   COLOR MainFG, MainBG                  'first display all of the choices
  959.   LOCATE 25, 2                          'and update the status line
  960.   PRINT "Up/Down/Space: Select files    Tab: Edit destination    F2: Begin    Esc: Quit";
  961.  
  962.   COLOR NormFG, NormBG
  963.   FOR Temp = 1 TO NumFiles
  964.     LOCATE 4 + Temp, 28, 0              'and turn off the cursor
  965.     PRINT FileNames$(Temp);
  966.   NEXT
  967.  
  968.   IF Choice = 0 THEN Choice = 1         'start at element 1 if first time
  969.   IF Choice > UBOUND(FileNames$) THEN Choice = 1 'or if past the end
  970.  
  971.   DO
  972.     LOCATE 4 + Choice, 28               'redraw current choice highlighted
  973.     COLOR HiFG, HiBG
  974.     PRINT FileNames$(Choice);
  975.     DO
  976.       KeyHit$ = INKEY$                  'see what they want to do
  977.     LOOP UNTIL LEN(KeyHit$)             'wait for a keypress
  978.    
  979.     IF LEN(KeyHit$) = 1 THEN            'set ExitCode based on the type of
  980.       ExitCode = ASC(KeyHit$)            'key (extended or not) they pressed
  981.     ELSE
  982.       ExitCode = -ASC(MID$(KeyHit$, 2))
  983.     END IF
  984.    
  985.     IF ExitCode = 32 THEN               'spacebar
  986.       IF MidChar%(FileNames$(Choice), 2) = 251 THEN 'if it's now checked
  987.         Temp = 32                       'remove the check mark
  988.       ELSE
  989.         Temp = 251                       'else add a check mark
  990.       END IF
  991.       CALL MidCharS(FileNames$(Choice), 2, Temp)
  992.       ExitCode = -80                    'select the next file automatically
  993.     END IF
  994.  
  995.     SELECT CASE ExitCode
  996.       CASE -80                          'Down Arrow
  997.         GOSUB Deselect
  998.         Choice = Choice + 1
  999.         IF Choice > NumFiles THEN Choice = 1
  1000.       CASE -79                          'End key
  1001.         GOSUB Deselect
  1002.         Choice = NumFiles
  1003.       CASE -72                          'Up Arrow
  1004.         GOSUB Deselect
  1005.         Choice = Choice - 1
  1006.         IF Choice = 0 THEN Choice = NumFiles
  1007.       CASE -71                          'Home
  1008.         GOSUB Deselect
  1009.         Choice = 1
  1010.       CASE -60                          'F2
  1011.         EXIT SUB
  1012.       CASE 27                           'Escape
  1013.         EXIT SUB
  1014.       CASE 9                            'Tab
  1015.         LOCATE Choice + 4, 33
  1016.         GOSUB Deselect
  1017.         EXIT SUB
  1018.       CASE ELSE                         'this is needed for QB 4.0 only
  1019.     END SELECT
  1020.   LOOP
  1021.  
  1022. Deselect:                               're-paint the current choice, so it
  1023.   LOCATE Choice + 4, 28                 '  won't appear active
  1024.   COLOR NormFG, NormBG
  1025.   PRINT FileNames$(Choice);
  1026.   RETURN
  1027.  
  1028. END SUB
  1029.  
  1030. SUB SetDrive (Drive$) STATIC
  1031.    
  1032.   Regs.AX = &HE00                       'DOS Set Drive service in AH
  1033.   Regs.DX = ASC(UCASE$(Drive$)) - 65    'DL = 0 for A:, 1 for B:, and so on
  1034.  
  1035.   CALL Interrupt(DOS, Regs, Regs)       'see if there's a match
  1036.  'CALL Interrupt(DOS, Regs)             'use this with P.D.Q.
  1037.  
  1038. END SUB
  1039.  
  1040. FUNCTION SourceDir$ STATIC
  1041.  
  1042.   Temp$ = ExeName$                  'get the directory we're running from
  1043.  
  1044.   FOR X = LEN(Temp$) TO 1 STEP -1   'isolate the drive letter and path
  1045.     Temp = MidChar%(Temp$, X)       '  (strip off the name PC-SETUP.EXE)
  1046.     IF Temp = 58 OR Temp = 92 THEN  'look for a colon or a backslash
  1047.       SourceDir$ = LEFT$(Temp$, X)  'by searching for ":" or "\" this will
  1048.       EXIT FOR                      ' work even if the program is renamed
  1049.     END IF
  1050.   NEXT
  1051.  
  1052. END FUNCTION
  1053.  
  1054. SUB StuffBuf (Cmd$) STATIC
  1055.        
  1056.   '----- Set the segment for poking, define the buffer head and tail, and
  1057.   '      then poke each character into the keyboard buffer.
  1058.  
  1059.   Temp = LEN(Cmd$)
  1060.  
  1061.   DEF SEG = 0
  1062.   POKE &H41A, &H1E
  1063.   POKE &H41C, &H1E + Temp * 2
  1064.    
  1065.   FOR X = 1 TO Temp
  1066.     POKE &H41C + X * 2, ASC(MID$(Cmd$, X))
  1067.   NEXT
  1068.  
  1069. END SUB
  1070.  
  1071.